Skip to content

Update specification for directives for sys.implementation and sys.platform checks. - #2173

Open
Josverl wants to merge 4 commits into
python:mainfrom
Josverl:specs/sys.implementation.name
Open

Update specification for directives for sys.implementation and sys.platform checks.#2173
Josverl wants to merge 4 commits into
python:mainfrom
Josverl:specs/sys.implementation.name

Conversation

@Josverl

@Josverl Josverl commented Feb 12, 2026

Copy link
Copy Markdown

This PR adds additional detail to the specification for Version and Platform checking.
Specificially it aims to add support for typechers to add support for :

  • checks on sys.implementation.name
  • membership checks (in tuple)
  • negative membership checks ( not in tuple)

References :

@python-cla-bot

python-cla-bot Bot commented Feb 12, 2026

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated

* Equality: ``sys.platform == "linux"``
* Inequality: ``sys.platform != "win32"``
* Membership: ``sys.platform in ("linux", "darwin")``

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced that containment should be supported here. There's already a way to express this in a way that all tools today support. I understand the argument that this is less verbose, but it's not that common for checks to include more than one platform, so I don't think there's a compelling argument to force all tools to support this additional form.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For deciding on issues like this it would be helpful to have a little summary of what type checkers currently support (like what I did in https://discuss.python.org/t/spec-change-clarify-that-tuple-should-not-be-prohibited-as-an-argument-to-type/105590/7). I'll gather a few variants and summarize it.

I think part of this change will be codifying what is already supported universally, and another part will be adding support for more features. The first group should be uncontroversial, and in the second group we should only force work on type checker authors if there's a clear use case.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible this would really help with creating readable and maintainable type stubs for MicroPython.
even with the proposed sys.implementation.name check we still see significant API differences due the underlying MCU vendor SDKs being significantly different.
Though MicroPython tries to abstract much of these differences away that is not entirly possible as the underlying SDK or hardware is simply different. This is a common source of errors when code is ported from one MCU architecture to another.
For instance Timers are available on all platforms, but with many different default values.

While the below would work

# machine.pyi 
class Timer():
    """"Timer object"""

    if sys.implementation.name == "micropython" and (sys.platform == "esp32" or sys.platform ==  "mimxrt" or  sys.platform ==  "rp2" or sys.platform ==  "samd" or sys.platform ==  "stm32" or sys.platform ==  "alif" or sys.platform ==  "webassembly"): 
        @overload
        def __init__(
            self,
            id: int,
            /,
            *,
            mode: int = PERIODIC,
            period: int | None = None,
            callback: Callable[[Timer], None] | None = None,
            hard: bool | None = None,
        ):...
    elif sys.implementation.name == "micropython" and (sys.platform == "esp8266" or sys.platform ==  "unix" or  sys.platform ==  "windows"  or sys.platform ==  "zephyr"): 
        @overload
        def __init__(
            self,
            id: int = -1,
            /,
            *,
            mode: int = PERIODIC,
            period: int | None = None,
            callback: Callable[[Timer], None] | None = None,
        ):...

the below is much simpler to understand and maintain.

class Timer():
    """"Timer object"""

    if sys.implementation.name == "micropython" and (sys.platform in ("esp32", "mimxrt", "rp2", "samd", "stm32", "alif", "webassembly")): 
        @overload
        def __init__(
            self,
            id: int,
            /,
            *,
            mode: int = PERIODIC,
            period: int | None = None,
            callback: Callable[[Timer], None] | None = None,
            hard: bool | None = None,
        ):...
    elif sys.implementation.name == "micropython" and (sys.platform in ("esp8266", "unix", "windows", "zephyr")): 
        @overload
        def __init__(
            self,
            id: int = -1,
            /,
            *,
            mode: int = PERIODIC,
            period: int | None = None,
            callback: Callable[[Timer], None] | None = None,
        ):...

I think it would be reasonable to explicitly restrict this to "a tuple of literal strings",assuming that simplies the implementation.
Negative membership option could also be omitted , I think that would still be sufficient.

@AlexWaygood AlexWaygood Feb 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully agree with Eric here FWIW. Adding support for this will add significant complexity to type checkers, and this is the first time I've seen it requested. If we want to ask type checkers to add support for in/not in comparisons with sys.platform and sys.implementation.name, I think it should be a wholly separate proposal to the proposal that asks type checkers to add initial support for comparisons against sys.implementation.version and sys.implementation.name in the same way that they already do for sys.version_info and sys.platform.

Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated

* Equality: ``sys.implementation.name == "cpython"``
* Inequality: ``sys.implementation.name != "cpython"``
* Membership: ``sys.implementation.name in ("pypy", "graalpy")``

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here again, I don't think there's good justification for supporting a containment operator.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I addedd this for consistency, but I'm OK to remove sys.implementation.name in ("pypy", "graalpy") from the spec to simplify the implementation.

@srittau srittau added the topic: typing spec For improving the typing spec label Feb 20, 2026
@Josverl
Josverl force-pushed the specs/sys.implementation.name branch 2 times, most recently from 4151e84 to d7fbb9e Compare February 21, 2026 17:09
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated

Supported patterns:
* ``sys.platform <comparison> <string literal>``
* ``sys.platform in <tuple of string literals>``

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or not in?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the not / and / or are described further down to avoid neededing to repeat then for every possible clause, although != is repeated.
I could move that section to the top if that is clearer.


Multiple comparisons can be combined with:
* A not unary operator
* An and or or binary operator

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's different, it would allow not (sys.platform in ("a", "b")), not sys.platform not in ("a", "b").

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point, then I think it is clearer to just enumerate all option directly,

Supported patterns:
    * ``sys.platform == <string literal>``
    * ``sys.platform != <string literal>``
    * ``sys.platform in <tuple of string literals>``
    * ``sys.platform not in <tuple of string literals>``

Comment thread docs/spec/directives.rst Outdated
@Josverl
Josverl force-pushed the specs/sys.implementation.name branch from d7fbb9e to 62d90dc Compare February 22, 2026 12:56
@Josverl

Josverl commented Feb 22, 2026

Copy link
Copy Markdown
Author

I have updated the text to better clarify the exact comparisons for each of the supported attributes,
excluded the use of named attributes,
and did some more word-smithing to reduce unneeded verbosity.
I have also added highlighting to the examples, as a raiding aide.

@Josverl
Josverl force-pushed the specs/sys.implementation.name branch from 62d90dc to ff338e3 Compare February 22, 2026 13:08
Comment thread docs/spec/directives.rst Outdated
Comment thread docs/spec/directives.rst Outdated
@Josverl

Josverl commented Mar 30, 2026

Copy link
Copy Markdown
Author

Dear TC,
Is there a decision on this PR yet, or have I missed a step in the process?

If so kindly let me know how to proceed.

@JelleZijlstra JelleZijlstra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm generally supportive but I think this change needs some more editing, and I'd like support in at least some type checkers (at least an open PR) before we start mandating this pattern.

Comment thread docs/spec/directives.rst Outdated
Josverl and others added 3 commits April 3, 2026 21:21
…atform checks.

Signed-off-by: Jos Verlinde <Jos.Verlinde@Microsoft.com>
Formatting improvements.

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
…sion comparisons.

Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
@Josverl
Josverl force-pushed the specs/sys.implementation.name branch from c575c15 to c951b00 Compare April 3, 2026 19:21
@Josverl
Josverl requested review from carljm and erictraut April 3, 2026 19:23
@Josverl

Josverl commented Aug 9, 2026

Copy link
Copy Markdown
Author

Dear typing council,
I have been waiting patiently , but there seems to be no progress on this.
As far as I can see I have actioned - or responded - to all feedback,
Reviewers have not responded to the updates they requested. Eric has stepped down for the TC since - so that is understandable.

However now it appears that this PR is in a Catch-22 situation;
Waiting for One or more typecheckers to implement this - but for typecheckers to implement this it should be in the spec.

Lacking response I plan close this in a few weeks

Comment thread docs/spec/directives.rst
Comment on lines +198 to +199
* ``sys.platform in <tuple of string literals>``
* ``sys.platform not in <tuple of string literals>``

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ruff (PLR6201) will report an error for this tuple membership check, and I tend to agree with ruff that a set literal would be more idiomatic here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the wild I've seen things like sys.platform.startswith("freebsd") a couple of times, because in this case there's also a version number in the platform string (e.g. "freebsd8"). So how about we also allow sys.platform.startswith(<string literal>)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty already supports sys.platform.startswith; I don't have any objection there.

Supporting set literals will be a little tricky in ty, but should be doable, and I'm not opposed to requiring support for it. I don't think the performance motivation of PLR6201 typically applies much to sys.version_info comparisons, but it is awkward if this rule is generally being applied in a codebase and has to be specifically ignored for sys.version_info checks.

Comment thread docs/spec/directives.rst
* ``sys.version_info >= <2-tuple>``
* ``sys.version_info < <2-tuple>``

Comparisons checks are only supported against the first two elements of the version tuple.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned a while back in https://discuss.python.org/t/proposal-to-improve-support-for-other-python-platforms-in-the-typing-specification/91877/13, there are genuine use cases for also comparing against the patch version, e.g. in typeshed: https://github.com/python/typeshed/blob/4f84ac178fb23541475f9038840c5b149834374f/stdlib/heapq.pyi#L9-L11

So I propose we loosen this restriction to also allow for 3-tuples.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As also discussed in that thread, I think mandating support for 3-tuple comparisons opens up a can of worms regarding what "support" for 3-tuple comparisons even means. We cannot require type checkers to always precisely support such comparisons, because type checkers may not have access to precise micro Python versions: type checker configurations (and pyproject.toml etc) typically do not specify micro versions, and I don't think we want to require users to always start requiring them. (Whereas I think in practice all type checkers already do require users to always provide major/minor version in some way, explicitly or implicitly.) So what is the expected behavior if micro Python version is not available to the type checker?

For example, ty currently "supports" if sys.version_info >= (3, 14, 1) (when Python version is configured to 3.14) in the "sound" way, by treating it as "unknown truthiness" and considering that either path may be taken (since actual Python version could be 3.14.0 or 3.14.1+). Would that behavior be considered to meet the bar required of type checkers here, or not? In some sense that behavior is a "regression" from writing if sys.version_info >= (3, 14), where ty would always infer a definite truthiness.

("Get the micro version from the runtime Python environment" is not a reliable fallback. It requires running the Python executable, which is avoided by at least ty since it can ~double the overall runtime of checking a small project using a fast type checker. And type checking -- e.g. for a small project without dependencies -- doesn't even necessarily require a Python runtime environment to begin with.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My instinct in case of sys.version_info >= (x, y, z) and sys.version_info < (x, y, z) when statically only x and y are known is to use the latest available z, because that's what I expect to be most common in practice. I'm sure there will be edge-cases that this could be problematic for, but on the macro I this this'll solve more problems that it'll cause.

But having just written this, I now see how the complexity of this solution might indeed not be worth it for a problem as niche as rare as this one.

So how about we don't require type-checkers to support 3-tuples, but explicitly state that type-checkers may choose to also support 3-tuples?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the latest available z

Type checkers don't really have a way to know the latest available z at any point in time, either, so I think this rule would have to be something more like "assume z is ", or something like that. Which will lead to weird behavior in some edge cases (imagine a new feature is introduced in 3.15 beta and someone introduces an if sys.version_info <= (3, 14) in typeshed when it is introduced, and immediately type checkers all start assuming every user is running 3.15 beta or later). But you may be right that in practice it does the right thing most of the time.

So how about we don't require type-checkers to support 3-tuples, but explicitly state that type-checkers may choose to also support 3-tuples?

I think in general the assumption of this entire PR should be that type checkers can always choose to support more than what is required here, and the wording in all sections should be such as to leave that possibility open. If this is the route we take, I don't know that we need to specially emphasize it for 3-tuples, but we can.

Comment thread docs/spec/directives.rst
Comment on lines +189 to +190
else:
# Python 3.11 and lower

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to include elif in this example, so that it's clear that elif is also expected to be supported, and not just if and else. For example as:

Suggested change
else:
# Python 3.11 and lower
elif sys.version_info >= (3, 11):
# Python 3.11.*
else:
# Python 3.10 and lower

Comment thread docs/spec/directives.rst
Configuration
^^^^^^^^^^^^^

Type checkers should provide configuration or CLI options to specify target sys.version, sys.platform, sys.implementation.name and sys.implementation.version.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Type checkers should provide configuration or CLI options to specify target sys.version, sys.platform, sys.implementation.name and sys.implementation.version.
Type checkers should provide configuration or CLI options to specify target ``sys.version``, ``sys.platform``, ``sys.implementation.name`` and ``sys.implementation.version``.

@davidhalter

davidhalter commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Waiting for One or more typecheckers to implement this - but for typecheckers to implement this it should be in the spec.

This is not the case at all. It is very typical for type checkers to implement features even if they are not in the spec. It is very helpful to have feedback from type checker authors to be able to understand potential problems with spec changes.

I think this has worked quite well in the past.and if a feature is useful enough there is usually a type checker that will implement it.

@carljm carljm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience, @Josverl. I'm not sure we've fully reached consensus yet on what should be supported here, but I also generally support making this clearly specified. I also think this still needs some editing to give a clear and unambiguous presentation of the requirements.

Comment thread docs/spec/directives.rst Outdated
* ``sys.version_info < <2-tuple>``

Comparisons checks are only supported against the first two elements of the version tuple.
Use of named attributes is not supported.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Use of named attributes is not supported.
Support for checks against named attributes of `sys.version_info` is not required.

Comment thread docs/spec/directives.rst
* ``sys.version_info >= <2-tuple>``
* ``sys.version_info < <2-tuple>``

Comparisons checks are only supported against the first two elements of the version tuple.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As also discussed in that thread, I think mandating support for 3-tuple comparisons opens up a can of worms regarding what "support" for 3-tuple comparisons even means. We cannot require type checkers to always precisely support such comparisons, because type checkers may not have access to precise micro Python versions: type checker configurations (and pyproject.toml etc) typically do not specify micro versions, and I don't think we want to require users to always start requiring them. (Whereas I think in practice all type checkers already do require users to always provide major/minor version in some way, explicitly or implicitly.) So what is the expected behavior if micro Python version is not available to the type checker?

For example, ty currently "supports" if sys.version_info >= (3, 14, 1) (when Python version is configured to 3.14) in the "sound" way, by treating it as "unknown truthiness" and considering that either path may be taken (since actual Python version could be 3.14.0 or 3.14.1+). Would that behavior be considered to meet the bar required of type checkers here, or not? In some sense that behavior is a "regression" from writing if sys.version_info >= (3, 14), where ty would always infer a definite truthiness.

("Get the micro version from the runtime Python environment" is not a reliable fallback. It requires running the Python executable, which is avoided by at least ty since it can ~double the overall runtime of checking a small project using a fast type checker. And type checking -- e.g. for a small project without dependencies -- doesn't even necessarily require a Python runtime environment to begin with.)

Comment thread docs/spec/directives.rst
* ``sys.version_info >= <2-tuple>``
* ``sys.version_info < <2-tuple>``

Comparisons checks are only supported against the first two elements of the version tuple.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Comparisons checks are only supported against the first two elements of the version tuple.
Comparison checks are only supported against the first two elements of the version tuple.

Comment thread docs/spec/directives.rst
* ``sys.version_info < <2-tuple>``

Comparisons checks are only supported against the first two elements of the version tuple.
Use of named attributes is not mandated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Use of named attributes is not mandated.
Type checkers are not expected to support comparisons with named attributes of `sys.version_info`.

Comment thread docs/spec/directives.rst
Comment on lines +198 to +199
* ``sys.platform in <tuple of string literals>``
* ``sys.platform not in <tuple of string literals>``

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty already supports sys.platform.startswith; I don't have any objection there.

Supporting set literals will be a little tricky in ty, but should be doable, and I'm not opposed to requiring support for it. I don't think the performance motivation of PLR6201 typically applies much to sys.version_info comparisons, but it is awkward if this rule is generally being applied in a codebase and has to be specifically ignored for sys.version_info checks.

Comment thread docs/spec/directives.rst
* ``sys.implementation.version >= <2-tuple>``
* ``sys.implementation.version < <2-tuple>``

Comparisons checks are only supported against the first two elements of the implementation version tuple.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Comparisons checks are only supported against the first two elements of the implementation version tuple.
Comparison checks are only supported against the first two elements of the implementation version tuple.

Comment thread docs/spec/directives.rst
Comment on lines +261 to +262
``sys.implementation.version`` is a tuple, in the same format as sys.version_info. However it represents the version of the Python implementation rather than the version of the Python language.
This has a distinct meaning from the specific version of the Python language to which the currently running interpreter conforms. For CPython this is the same as `sys.version_info`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it belongs more as introductory text in this section, rather than as a note.

Comment thread docs/spec/directives.rst
No support for complex expressions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Type checkers are only required to support the above patterns, and are not required to evaluate complex expressions involving these variables.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the expectation is that and and or boolean expressions are supported, that should be explicitly discussed here. Rather than saying "are not required to evaluate complex expressions" (which is vague), we should simply specify the forms that must be supported, and that no other form is required.

Comment thread docs/spec/directives.rst
Type checkers are only required to support the above patterns, and are not required to evaluate complex expressions involving these variables.
For example, the pattern ``sys.platform == "linux"`` is supported but other syntax variants such as ``platform == "linux"`` and ``"win" not in sys.platform`` are not mandated.

Therefore checkers are **not required** to understand obfuscations such as:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "reversed" example is an obfuscation, but "win" not in sys.platform is not an obfuscation, and from sys import platform; platform == "linux" is definitely not an obfuscation.

Let's avoid value judgments and just give a clear presentation of the forms that are supported, and some example forms that type checkers are not required to support.

Comment thread docs/spec/directives.rst
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Type checkers are only required to support the above patterns, and are not required to evaluate complex expressions involving these variables.
For example, the pattern ``sys.platform == "linux"`` is supported but other syntax variants such as ``platform == "linux"`` and ``"win" not in sys.platform`` are not mandated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why we need to give exactly the same examples both in prose and then below in a code snippet.

@Josverl

Josverl commented Aug 10, 2026

Copy link
Copy Markdown
Author

Thanks all for the renewed attention and comments.

  • I'll try to clarify where I can do
  • and keep a list of topics that need a decision one way or another

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic: typing spec For improving the typing spec

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants